﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections;
using TratamentoRIWikipedia.Indexacao;
using TratamentoRIWikipedia.processamento;

namespace TratamentoRIWikipedia.modelagem
{
    class ModeloVetorial
    {
        public static int QtdDocColecao; // Valor atribuito no out da leitura do indice 
        public static Dictionary<string, ArrayList> indiceMemoriaPri = ModelagemGeral.LerIndiceMemoriaFrequencia(out QtdDocColecao);//Monta o indice na memoria principal 


        /// <summary>
        /// Processa o texto da busca e retorna o resultado
        /// </summary>
        /// <param name="TextoBuscado">Texto digitado no campo de busca</param>
        public static Dictionary<string, string> BuscaVetorial(string TextoBuscado)
        {

            bool buscaOR = false;
            ArrayList itensDaBusca;
            TextoBuscado = ModelagemGeral.preProcessaConsulta(TextoBuscado, out buscaOR, out itensDaBusca);

            List<Dictionary<int, int>> listasOcorrencia = new List<Dictionary<int, int>>();
            Dictionary<string, string> documentoBuscado = new Dictionary<string, string>();
            Dictionary<int, double[]> listaVetoresDoc = new Dictionary<int, double[]>();  //[id documento, peso do documento]
            SortedList<double, int> VetoresDocTDimensional = new SortedList<double, int>();
            double[] vetorConsulta = new double[itensDaBusca.Count];
            double[] vetorDoc = new double[itensDaBusca.Count];

            int freq; //frequencia do termo k no documento d
            int QtdDocTermo; //número de documentos que contêm o termo k
            int cont = 0;

            //Busca-se a lista de ocorrências de cada palavra individualmente no índice
            foreach (string item in itensDaBusca)
            {
                listasOcorrencia.Add(BuscaListaOcorrencia(item));//Lista com [id documento, frequencia palavra]

                if (listasOcorrencia.Count > 0)
                {
                    return documentoBuscado;
                }
                else
                {
                    if (listasOcorrencia[cont].Count > 0)
                    {
                        //Calcula o TFxIDF do termo da consulta
                        double TFxIDFConsulta = caculaTFxIDF(listasOcorrencia[cont].Values.Sum(), listasOcorrencia[cont].Values.Count());
                        //Adiciona no vetor de consulta
                        vetorConsulta[cont] = TFxIDFConsulta;
                    }

                }
                cont++;
            }

            int posi = 0;
            //Pecorre a listas de ocorrencia da consulta
            foreach (Dictionary<int, int> lista in listasOcorrencia)
            {
                //Para cada documento distinto presente nas listas de ocorrências retornadas:
                foreach (KeyValuePair<int, int> item in lista)
                {
                    //
                    QtdDocTermo = lista.Count();
                    freq = item.Value;
                    //ligo umar


                    if (!listaVetoresDoc.ContainsKey(item.Key))
                    {
                        //Representa o documento através de um vetor t-dimensional passa o valor do TFxIDF [Calcula o peso de cada termo ]
                        vetorDoc[posi] = caculaTFxIDF(freq, QtdDocTermo);
                        listaVetoresDoc.Add(item.Key, vetorDoc);
                        vetorDoc = new double[itensDaBusca.Count];
                    }
                    else
                    {
                        listaVetoresDoc[item.Key][posi] = caculaTFxIDF(freq, QtdDocTermo);
                    }
                }
                posi++;
            }

            //3-Calcula-se a similaridade cosseno entre a consulta e cada documento distinto
            foreach (KeyValuePair<int, double[]> vector in listaVetoresDoc)
            {
                double similaridade = similaridadeCos(vector.Value, vetorConsulta);

                //<<<< quando tem um termo só ta dando duplicate>>>
                //4-Ordena-se os documentos de acordo com essa similaridade
                VetoresDocTDimensional.Add(similaridade,vector.Key);
            }

          

            var result = VetoresDocTDimensional.OrderByDescending(a => a.Key);

            foreach (KeyValuePair<double, int> item in result)
            {
                documentoBuscado = ModelagemGeral.BuscaDocMemoria(item.Value);

                ////Exibe resultado da Bsuca
                //foreach (KeyValuePair<string, string> termos in documentoBuscado)
                //{

                //    Console.WriteLine();
                //    Console.Write(termos.Key.ToUpper());
                //    Console.Write("  peso => " + item.Key + "\n");
                //    Console.WriteLine(termos.Value.Length >= 500 ? termos.Value.ToString().Substring(1, 500) : termos.Value.ToString());
                //}
            }

            return documentoBuscado;
        }

        public static double caculaTFxIDF(int freq, int QtdDocTermo)
        {
            double TF = 0.0;
            double IDF = 0.0;

            if (freq > 0)
                TF = 1 + Math.Log10(freq);
            else
                TF = 0;

            IDF = Math.Log(QtdDocColecao / QtdDocTermo);


            return TF * IDF;
        }


        public static double similaridadeCos(double[] V1, double[] V2)
        {
            int MaiorDoc = 0;

            MaiorDoc = ((V2.Length < V1.Length) ? V2.Length : V1.Length);

            double multipli = 0.0d;
            double potencia1 = 0.0d;
            double potencia2 = 0.0d;

            for (int n = 0; n < MaiorDoc; n++)
            {
                multipli += V1[n] * V2[n];
                potencia1 += Math.Pow(V1[n], 2);
                potencia2 += Math.Pow(V2[n], 2);
            }

            return (multipli / (Math.Sqrt(potencia1) * Math.Sqrt(potencia2)));
        }


        /// <summary>
        /// Busca-se a lista de ocorrências da palavra
        /// </summary>
        /// <param name="palvara">Texto digitado no campo de busca</param>
        public static Dictionary<int, int> BuscaListaOcorrencia(string palvara)
        {

            //lista é  [id documento, frequencia da palavra de entrada nesse doc]
            Dictionary<int, int> listOcorre = new Dictionary<int, int>();

            try
            {
                //Se o indice possui a palavra, pecorre o indice dessa palavra adicionando na lista de 
                // ocorrencia o id do doc e a frequencia da palavra
                ArrayList ValueIndex = null;

                indiceMemoriaPri.TryGetValue(palvara, out ValueIndex);

                if (indiceMemoriaPri.ContainsKey(palvara))

                    for (int i = 0; i < ValueIndex.Count; i++)
                    {

                        string[] separador = new string[] { "(", ")", "," };
                        string[] informaTermo = ValueIndex[i].ToString().Split(separador, StringSplitOptions.RemoveEmptyEntries);

                        listOcorre.Add(int.Parse(informaTermo[0]), int.Parse(informaTermo[1]));

                    }
            }
            catch (Exception e)
            {

                throw e;
            }

            return (listOcorre);
        }
    }
}
